package com.sulent.frame.file.utils;

import cn.hutool.crypto.digest.MD5;
import org.springframework.util.DigestUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @ClassName FileShardingDemo
 * @Description: TODO
 * @Author Submerge.
 * @Since 2024/1/31 15:59
 * @Version 1.0
 */
public class FileShardingDemo {

    public static void main(String[] args) throws IOException {

        String shardingFile = "D:\\data\\自研数据中台.docx";
        sharding(shardingFile, "D:\\data\\sharding4\\");
        //原始文件MD5
        String s = calcFileMd5(shardingFile);
        System.out.println(s);

//        merge("D:\\data\\sharding\\","C:\\Users\\Digital\\Desktop\\产品数据中台\\数据中台产品\\产品推广.pptx","产品推广.pptx","D:\\data\\merge\\");


    }

    public static String calcFileMd5(String filePath) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            FileInputStream fis = new FileInputStream(filePath);

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead);
            }

            byte[] digest = md.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b & 0xff));
            }

            fis.close();
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    public static void sharding(String shardingFile, String chunkPath) throws IOException {
        File sourceFile = new File(shardingFile);
        System.out.println(sourceFile.length());
        //分片文件路径
        File chunkFolder = new File(chunkPath);
        if (!chunkFolder.exists()) {
            chunkFolder.mkdirs();
        }
        //分片文件大小
        long chunkSize = 1024 * 1024 * 50;
        //分片数量[向上取整，1.5M则需要两块分片传输]
        long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize);
        System.out.println("分块总数：" + chunkNum);
        //缓冲区大小
        byte[] b = new byte[1024];
        //使用RandomAccessFile访问文件["r"，读]
        RandomAccessFile raf_read = new RandomAccessFile(sourceFile, "r");
        //将文件分片
        for (int i = 0; i < chunkNum; i++) {
            //创建分片文件
            File file = new File(chunkPath + i);
            if (file.exists()) {
                file.delete();
            }
            boolean newFile = file.createNewFile();
            if (newFile) {
                //如果创建成功则向分片文件中写入数据
                RandomAccessFile raf_write = new RandomAccessFile(file, "rw");
                int len = -1;
                while ((len = raf_read.read(b)) != -1) {
                    //从缓冲区读取数据
                    raf_write.write(b, 0, len);
                    if (file.length() >= chunkSize) {
                        break;
                    }
                }
                raf_write.close();
                System.out.println("完成分片" + i);
            }
        }
        raf_read.close();
    }

    public static void merge(String shardingPath, String originalFile, String fileName, String mergePath) throws IOException {
        //片文件目录
        File chunkFolder = new File(shardingPath);
        //原始文件
//        File originalFile = new File("D:\\系统默认\\桌面\\gra-res\\mv\\test.mp4");
        //合并后的文件
        File mergeFile = new File(mergePath + fileName);
        if (mergeFile.exists()) {
            mergeFile.delete();
        }
        //创建新的合并文件
        mergeFile.createNewFile();
        //用于写文件
        RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw");
        //指针指向文件顶端
        raf_write.seek(0);
        //缓冲区
        byte[] b = new byte[1024];
        //分块列表
        File[] fileArray = chunkFolder.listFiles();
        //转成集合便于排序【保证数据正确，以便可以恢复数据】
        List<File> fileList = Arrays.asList(fileArray);
        //从小到大排序
        Collections.sort(fileList, Comparator.comparingInt(o -> Integer.parseInt(o.getName())));
        //合并文件
        for (File chunkFile : fileList) {
            RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "rw");
            int len = -1;
            while ((len = raf_read.read(b)) != -1) {
                raf_write.write(b, 0, len);
            }
            raf_read.close();
        }
        raf_write.close();
        //校验文件是否成功恢复【如果文件相同，则二者MD5值相同】
        try (
                FileInputStream fileInputStream = new FileInputStream(originalFile);
                FileInputStream mergeFileStream = new FileInputStream(mergeFile);
        ) {
            //原始文件MD5
            byte[] bytes = DigestUtils.md5Digest(fileInputStream);
            String originalMd5 = new String(bytes, Charset.defaultCharset());
            System.out.println("原始文件的MD5:" + originalMd5);
            //合并文件的md5
            String mergeFileMd5 = new String(DigestUtils.md5Digest(mergeFileStream));
            System.out.println("合并文件的MD5:" + mergeFileMd5);
            if (originalMd5.equals(mergeFileMd5)) {
                System.out.println("合并文件成功");
            } else {
                System.out.println("合并文件失败");
            }
        }

    }


}
